﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Swift;
using Utils;
using TB;

namespace Server {

    public static class ActorExtention {

        public static Timer Timer;
        public static CoroutineManager CM;

        public static void EatFood(this Actor a, Actor food, ActorFactory factory) {
            
            switch(food.Type) {
                case 1: {  // 升级
                        if(a.Blood < 3) {
                            a.Blood++;
                            factory.Refresh (a);
                        }
                    }
                    break;
                case 2: {  // 暂停
                        factory.PauseAllEnemy ();
                    }
                    break;
                case 3: {  // 炸弹
                        factory.KillAllByTag ("enemy");
                    }
                    break;
                case 4: {  // 无敌
                        a.CanKill = false;
                        int i = food.T;
                        Timer.AddEvent ("CanKill" + a.GetHashCode (), 0, 1000, (name) => {
                            if(a.Alive == false) {
                                Timer.RemoveEvent (name);
                                return;
                            }
                            i--;
                            if(i < 0) {
                                a.CanKill = true;
                                Timer.RemoveEvent (name);
                            }
                        });
                    }
                    break;
                case 5: {  // 铁锹

                        factory.KillAllByTag ("basewall");

                        for(int i = 23; i <= 25; i++) {
                            for(int j = 11; j <= 14; j++) {
                                if(i >= 24 && j >= 12 && j <= 13)
                                    continue;
                                factory.CreateActor (2, i, j, 0.5f, "basewall");
                            }
                        }

                        Timer.RemoveEvent ("food5_" + factory.GetHashCode ());

                        int t = food.T;
                        Timer.AddEvent ("food5_" + factory.GetHashCode (), 0, 1000, (name) => {
                            if(factory.Alive == false) {
                                Timer.RemoveEvent (name);
                                return;
                            }
                            t--;
                            if(t < 0) {
                                factory.KillAllByTag ("basewall");

                                for(int i = 23; i <= 25; i++) {
                                    for(int j = 11; j <= 14; j++) {
                                        if(i >= 24 && j >= 12 && j <= 13)
                                            continue;
                                        factory.CreateWoodWall (1001, i, j);
                                    }
                                }

                                Timer.RemoveEvent (name);
                            }
                        });
                    }
                    break;
                case 6: {  // 加生命
                        a.Life++;
                    }
                    break;
                default:
                    throw new Exception ("找不到 食物类型 = " + food.Type);
            }
            factory.RemoveActor(food);
        }

        public static void Attack(this Actor a, ActorFactory factory, Action<string> callback=null) {

            if(a.Cooling == true) {
                a.Cooling = false;

                Actor bullet = factory.CreateActor (10, a.AX, a.AY, 0.25f, "bullet-" + a.Tag, a.Dir);
                bullet.Owner = a;
                bullet.ATK = a.ATK;
                bullet.Speed = a.BulletSpeed;
                factory.NeedLocomotor (bullet);

                CM.Start (bullet.GoAhead ( factory, (obstacles) => {

                    bool flag = false;
                    for(int i = 0; i < obstacles.Count; i++) {
                        if(obstacles[i].CanKill == false) {
                            factory.RemoveActor (bullet);
                            continue;
                        }
                        if(obstacles[i].Tag == "enemy") {               // 如果是敌人
                            if(a.Tag == "enemy") {
                                continue;
                            } else {
                                if(obstacles[i].WithFood == true) {     // 如果携带着食物 
                                    obstacles[i].WithFood = false;
                                    if(callback != null) {
                                        callback ("food");
                                    } 
                                } else {
                                    obstacles[i].Blood -= bullet.ATK;
                                    if(obstacles[i].Blood <= 0) {
                                        callback ("enemy" + (obstacles[i].TID / 100 - 3) );
                                        factory.RemoveActor (obstacles[i]);
                                    } else {
                                        factory.Refresh (obstacles[i]);
                                    }
                                }
                            }
                        } else if(obstacles[i].Tag == "player1") {
                            if(a.Tag == "player1") {
                                continue;
                            } else if(a.Tag == "player2") {
                                
                                Timer.RemoveEvent ("punish" + obstacles[i].GetHashCode ());
                                obstacles[i].Paused = true;
                                Actor tmp = obstacles[i];
                                int t = a.ATK * 5;
                                Timer.AddEvent ("punish" + obstacles[i].GetHashCode (), 0, 1000, (name) => {
                                    if(obstacles[i].Alive == false) {
                                        Timer.RemoveEvent (name);
                                        return;
                                    }
                                    t--;
                                    if(t < 0) {
                                        tmp.Paused = false;
                                        Timer.RemoveEvent (name);
                                    }
                                });
                            } else {
                                obstacles[i].Blood -= bullet.ATK;
                                if(obstacles[i].Blood <= 0) {
                                    factory.RemoveActor (obstacles[i]);

                                    if(callback != null) {
                                        callback ("player1");
                                    }

                                } else {
                                    factory.Refresh (obstacles[i]);
                                }
                            }
                        } else if(obstacles[i].Tag == "player2") {
                            if(a.Tag == "player2") {
                                continue;
                            } else if(a.Tag == "player1") {

                                Timer.RemoveEvent ("punish" + obstacles[i].GetHashCode ());
                                obstacles[i].Paused = true;
                                Actor tmp = obstacles[i];
                                int t = a.ATK * 5;
                                Timer.AddEvent ("punish" + obstacles[i].GetHashCode (), 0, 1000, (name) => {
                                    if(obstacles[i].Alive == false) {
                                        Timer.RemoveEvent (name);
                                        return;
                                    }
                                    t--;
                                    if(t < 0) {
                                        tmp.Paused = false;
                                        Timer.RemoveEvent (name);
                                    }
                                });
                            } else {
                                obstacles[i].Blood -= bullet.ATK;
                                if(obstacles[i].Blood <= 0) {
                                    factory.RemoveActor (obstacles[i]);

                                    if(callback != null) {
                                        callback ("player2");
                                    }

                                } else {
                                    factory.Refresh (obstacles[i]);
                                }
                            }
                        } else {
                            if(obstacles[i].TID == 2) {  // 铁墙 
                                if(a.ATK == 1) {
                                    factory.RemoveActor (bullet);
                                    continue;
                                }
                            }

                            factory.RemoveActor (obstacles[i].GetImplicativeActor (bullet.Dir));
                            factory.RemoveActor (obstacles[i]);
                        }
                        flag = true;
                    }


                    if(flag == true || obstacles.Count == 0) {
                        factory.RemoveActor (bullet);
                    }

                }));


            }
        
        }
        
        /// <summary>
        /// 直走
        /// </summary>
        /// <param name="a"></param>
        /// <param name="callback"> 遇到障碍物，就回调 </param>
        /// <returns></returns>
        public static IEnumerator GoAhead(this Actor a, ActorFactory factory, Action<List<Actor>> callback = null) {
                List<Actor> obstacles;
                while(true) {
                    yield return null;

                    a.Move (0.5f / a.Speed);

                    if(factory.IsBlock (a, out obstacles)) {
                        if(callback != null) {
                            callback (obstacles);
                        }
                    }

                    if(a.Alive == false) {
                        break;
                    }
                }

            yield return null;
        }

        public static IEnumerator Move(this Actor a, int dir, ActorFactory factory) {

            if(a.Paused==false && a.Moving == false) {
                a.Moving = true;

                int k = a.Slide==false ? 1 : 3;  
                a.Dir = (DIR) dir;
                for(int i = 0; a.Alive==true && i < a.Speed; i++) {
                    a.Move (0.5f * k / a.Speed);

                    a.Slide = false;
                    List<Actor> foods = null;
                    if(
                        factory.IsBlock (a, (o) => {
                            if(o.Tag == "slide") {          // 滑地  
                                a.Slide = true;
                                return false;
                            } else if(o.Tag == "food") {    // 食物
                                if(foods == null) {
                                    foods = new List<Actor> ();
                                }
                                foods.Add (o);
                                return false;
                            }
                            return true;
                        })
                    ) {
                        break;
                    }

                    if(foods != null) {
                        for(int u = 0; u < foods.Count; u++) {
                            a.EatFood (foods[u], factory);
                        }
                    }

                    yield return null;
                }

                a.Moving = false;
            }

            yield return null;
        }

        public static IEnumerator Stupid(this Actor a, ActorFactory factory, Action<string> callback) {

            while(a.Alive) {

                if(a.Paused == false) {

                    a.Move (0.5f / a.Speed);

                    if(
                        factory.IsBlock (a, (o) => {
                            if(o.Tag == "slide" || o.Tag == "food") {
                                return false;
                            }
                            return true;
                        })
                    ) {
                        a.Dir = (DIR) RandomUtils.Random (0, 4);
                    }

                    if(RandomUtils.Random (0, 100) > 90) {
                        a.Attack (factory, callback);
                    }

                }
                yield return null;
            }

            yield return null;
        }

        public static void StartAI(this Actor a, ActorFactory factory, Action<string> callback) {

            switch(a.AI) {
                default:
                    CM.Start (Stupid(a, factory, callback));
                    break;
            }

        }

    }

    public class Game : Port, IFrameDrived {

        public Game() {
            Console.WriteLine ("一个新的游戏正在待命： Game" + this.GetHashCode());
        }

        ~Game() {
            Console.WriteLine ("一个游戏正被终止： Game" + this.GetHashCode());
            RemoveComponent ("Game" + this.GetHashCode ());
        }

        public int Status {
            get {
                if((conn[0] == null || !conn[0].IsConnected) && (conn[1] == null || !conn[1].IsConnected))
                    return 0;
                return 1;
            }
            set {
                if(value == 0) {
                    for(int i = 0; i < 2; i++) {
                        if(conn[i] != null) {
                            conn[i].Close ();
                            conn[i] = null;
                        }
                    }
                } 
            }
        }

        Connection[] conn = new Connection[2];
        ActorFactory factory;
        int num = 0;
        public int Num {
            get {
                return num;
            }
        }

        int chapter = 1;
        int[,] enemy = new int[4, 2];
        int killNum = 0;

        Map map;
        Actor[] player = new Actor[2];

        public void Add(Connection _conn) {
            conn[num++] = _conn;
        }
        CoroutineManager cm;
        public void Start() { 
            for(int i=0; i<2; i++) {

                IWriteableBuffer buff = conn[i].BeginSend ("MatchHelper");
                buff.Write ("Match");
                // 匹配状态切换成加载状态，客户端开始切换场景，监听各种事件，准备就绪以后通知服务器
                buff.Write ("Loading");                     
                buff.Write ("Game" + this.GetHashCode ());  // 将游戏组件名通知客户端
                conn[i].End (buff);

            }

            bool[] ready = new bool[2];
            OnMessage ("Ready", (_conn, data) => {  // 客户端准备好 
            
                // 两个都准备好后，创建各种资源，一起发送数据
                if(_conn == conn[0]) ready[0] = true;
                else if(_conn == conn[1]) ready[1] = true;

                if(ready[0] && ready[1]) {

                    factory = new ActorFactory (conn);
                    Load ();

                }
            
            });

            /*
             * 玩家操作 type：int
             * 解析成二进制。
             * 第一位为 1，表示移动，为 0，表示不移动
             * 第二位、第三位 在有移动时表示方向，没移动时忽略
             * 第四位为 1，表示攻击，为 0，表示不攻击
             */
            cm = GetComponent ("CoroutineManager") as CoroutineManager;
            OnRequest ("Controller", (_conn, data, buff) => {
                int index = -1;
                int type = data.ReadInt ();
                if(_conn == conn[0]) {    // 玩家一 操作
                    index = 0;
                } else if(_conn == conn[1]) {  // 玩家二 操作
                    index = 1;
                }

                if(index != -1) {
                    if((type & 1) > 0) {  // 需要移动 
                        cm.Start (player[index].Move ((type & 6) >> 1, factory));
                    }

                    if((type & 8) > 0) {  // 需要攻击
                        player[index].Attack (factory, (tag) => {
                            if (tag == "food") {
                                GenFood();
                            }
                            else if (tag.StartsWith("enemy")) {
                                killNum++;
                                int t = (int)(tag[5] - '0');
                                enemy[t, index]++;
                            }
                        });
                    }

                    buff.Write ("OK");
                }
            });
        }

        void GenPlayer(int index, int tid, int x, int y) {

            if(player[index] == null) {
                player[index] = new Actor ();
            }

            if(player[index].Life > 0) {
                player[index].Life--;
                int num = player[index].Life;
                player[index] = factory.CreateActor (tid, x, y, 1f, "player"+(index+1), DIR.UP, 1);
                factory.NeedLocomotor (player[index]);
                player[index].Life = num;
            }
        }

        Actor stronghold;

        void Load() {  // 加载资源 

            GenPlayer (0, 100, 24, 8);
            GenPlayer (1, 200, 24, 16);

            map = new Map ();
            map.Load (chapter);

            // 创建地图资源，墙、水、草地、滑块
            
            for(int i = 0; i < 26; i++) {
                for(int j = 0; j < 26; j++) {
                    int tid = map.GetXY (i, j);
                    if(tid != 0) {

                        if(tid == 1) {     
                            factory.CreateWoodWall (1001, i, j);
                        } else if(tid == 2) {
                            factory.CreateActor (tid, i, j, 0.5f, "wall");
                        } else if(tid == 3) {
                            factory.CreateActor (tid, i, j, 1f, "water");
                        } else if(tid == 4) {
                            factory.CreateActor (tid, i, j, 1f, "grass");
                        } else if(tid == 5) {
                            factory.CreateActor (tid, i, j, 1f, "slide");
                        } 

                    }
                }
            }

            // 创建基地
            stronghold = factory.CreateActor (10000, 24, 12, 1f, "base");


            // 加载敌人
            cm.Start(GenEnemy ());

        }
    


        void GenFood() {

            int x, y;
            map.RandomPos (out x, out y); 

            int type = RandomUtils.Random(1, 7);
            Actor food = factory.CreateActor (20 + type, x, y, 0.66f, "food");
            food.Type = type;

            Timer timer = GetComponent ("Timer") as Timer;
            timer.AddEvent ("food" + food.GetHashCode (), 0, 1000, (name) => {
                if(food.Alive == false) {
                    timer.RemoveEvent (name);
                    return;
                }
                food.T--;

                if(food.T < 0) {
                    factory.RemoveActor (food);
                    timer.RemoveEvent (name);   
                }
            });

        }

        IEnumerator GenEnemy() {

            Timer timer = GetComponent ("Timer") as Timer;

            int type;
            bool withFood;
            while(map.Next (out type, out withFood)) {

                int blood = 1;
                if(type == 1)
                    blood = 2;
                else if(type == 3)
                    blood = 4;

                Actor a = factory.CreateActor ((3+type)*100, 0, 0, 1f, "enemy", DIR.UP, blood);
                a.WithFood = true;
                factory.NeedLocomotor (a);

                a.StartAI (factory, (tag) => {
                    if(tag == "player1") {
                        GenPlayer (0, 100, 24, 8);
                    } else if(tag == "player2"){
                        GenPlayer (1, 200, 24, 16);
                    }
                });

                bool wait = true;
                int t = 10;   // 生成敌人间隔时间
                timer.AddEvent ("gen_enemy_" + factory.GetHashCode (), 0, 1000, (name) => {
                    if(factory.Alive == false) {
                        timer.RemoveEvent (name);
                        return;
                    }
                    t--;
                    if(t < 0) {
                        wait = false;
                        timer.RemoveEvent (name);
                    }
                });

                while(wait == true) {
                    yield return null;
                }
            }

            yield return null;
        }

        public override void Close() {
            if (factory != null) {
                factory.Close();
            }
            base.Close ();
        }

        void Balance(bool pass) {
            for (int i = 0; i < 2; i++) {
                IWriteableBuffer buff = conn[i].BeginSend("Factory");
                buff.Write("Balance");
                buff.Write(chapter);
                buff.Write(pass ? 1 : 0);
                for (int u = 0; u < 4; u++) {
                    for (int v = 0; v < 2; v++) {
                        buff.Write(enemy[u, v]);
                    }
                }

                conn[i].End(buff);
            }

            factory.Close();

            if (pass) {
                chapter++;

                for (int i = 0; i < 2; i++) {
                    if (player[i].Alive) {
                        player[i].Life++;
                    }
                }

            }
        }

        public void OnTimeElapsed(int te) {

            if( factory != null && factory.Alive == true) {
                for(int i = 0; i < 2; i++) {
                    if(conn[i] != null) {
                        IWriteableBuffer buff = conn[i].BeginSend ("Factory");
                        buff.Write ("Status");
                        buff.Write (1);
                        buff.Write (player[0].Life);
                        buff.Write (player[1].Life);
                        buff.Write (map.GetEnemyNum ());
                        
                        conn[i].End (buff);
                    }
                }

                factory.OnTimeElapsed (te);

                if (stronghold.Alive == false || player[0].Alive == false && player[1].Alive == false) {
                    Balance(false);
                }

                if (killNum == 2) {
                    Balance(true);
                }
            }
        }
    }
}
